////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    LionsLairGuts.cpp                                            //
//  Developer:   George Chapman                                               //
//  Create date: 06/02/2005                                                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut for the    //
//               Australian Slot machine called Lion's Lair.                  //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        Copyright (c) 2005 - Realtime Gaming.  All rights reserved.         //
////////////////////////////////////////////////////////////////////////////////

#include "LionsLairGuts.h"

int32 LionsLairGuts::ms_multipliers[NUM_FEATURE_MULTIPLIERS];

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::LionsLairGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
LionsLairGuts::LionsLairGuts() 
{
	ms_multipliers[0] = MULTIPLIER_2X;
	ms_multipliers[1] = MULTIPLIER_3X;
	ms_multipliers[2] = MULTIPLIER_5X;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::~LionsLairGuts()   
///
/// This is the destructor for the class.
////////////////////////////////////////////////////////////////////////////
LionsLairGuts::~LionsLairGuts() 
{
}
    
////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// @return	 uint8      A payout from the SpinResult enumeration as a uint8.    
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void
LionsLairGuts::classifyStops(SpinLine* spinline, uint8* stops,uint8 bet, uint8 line, uint8& rtnresult, uint8& multiplier)
{

	SpinResult result              = EVT_LOSER; // spin result :)
	uint8      testIcon            = BLANK;     // Icon at current position
	uint8      iconThatIsNotLion   = BLANK;     // Holder for an Icon that is NOT Lion
    bool8      hasLion             = false;     // Indicates if Lion is part of the evaluation

    // By default everything pays at a x1 multiplier
    multiplier = 1;

	//Now, go through the rest, until the streak is over
	for ( uint8 i = 0; (i < getReelCount()); i++ )
    {
		testIcon = getIcon(getGameModeId(), i, stops[i])->iconid;

        // Check to see if it is Lion (we bump the count and continue looking)
        if ( testIcon == LION )
        {
            hasLion = true;
        }
		// As soon as we encounter a lair, the chain is broken.
		else if ( testIcon == LAIR )
		{
			break;
		}
        else
        {
            // If we haven't found our first non Lion, store it off.
            if ( iconThatIsNotLion == BLANK )
            {
                iconThatIsNotLion = testIcon;
            }
            // If we have found our fist non lion icon, and this icon was not lion
            // and it is not the same as the first non Lion icon, then our evaluation stops.
            else if ( iconThatIsNotLion != testIcon )
            {
                break;
            }
        }
    }

    switch ( iconThatIsNotLion )
    {
		case NINE:
			if ( i == 5 )
			{
                result = EVT_FIVE_9;
			}
			else if (i == 4)
            {
                result = EVT_FOUR_9;
			}
			else if (i == 3)
            {
                result = EVT_THREE_9;
			}
			break;
 
		case TEN:
			if ( i == 5 )
			{
                result = EVT_FIVE_10;
			}
			else if (i == 4)
            {
                result = EVT_FOUR_10;
			}
			else if (i == 3)
            {
                result = EVT_THREE_10;
			}
			break;

        case JACK:
			if ( i == 5 )
			{
                result = EVT_FIVE_J;
			}
			else if (i == 4)
            {
                result = EVT_FOUR_J;
			}
			else if (i == 3)
            {
                result = EVT_THREE_J;
			}
			break;

        case QUEEN:
			if ( i == 5 )
			{
                result = EVT_FIVE_Q;
			}
			else if (i == 4)
            {
                result = EVT_FOUR_Q;
			}
			else if (i == 3)
            {
                result = EVT_THREE_Q;
			}
            break;		
		
        case KING:
			if ( i == 5 )
			{
                result = EVT_FIVE_K;
			}
			else if (i == 4)
            {
                result = EVT_FOUR_K;
			}
			else if (i == 3)
            {
                result = EVT_THREE_K;
			}
			break;

        case ACE:
			if ( i == 5 )
			{
                result = EVT_FIVE_A;
			}
			else if (i == 4)
            {
                result = EVT_FOUR_A;
			}
			else if (i == 3)
            {
                result = EVT_THREE_A;
			}
            break;

        case PAW_PRINT:
			if ( i == 5 )
			{
                result = EVT_FIVE_PAW_PRINT;
			}
			else if (i == 4)
            {
                result = EVT_FOUR_PAW_PRINT;
			}
			else if (i == 3)
            {
                result = EVT_THREE_PAW_PRINT;
			}
			else if (i == 2)
            {
                result = EVT_TWO_PAW_PRINT;
			}
            break;

        case FLOWER:
			if ( i == 5 )
			{
                result = EVT_FIVE_FLOWER;
			}
			else if (i == 4)
            {
                result = EVT_FOUR_FLOWER;
			}
			else if (i == 3)
            {
                result = EVT_THREE_FLOWER;
			}
			else if (i == 2)
            {
                result = EVT_TWO_FLOWER;
			}
            break;

        case ZEBRA:
			if ( i == 5 )
			{
                result = EVT_FIVE_ZEBRA;
			}
			else if (i == 4)
            {
                result = EVT_FOUR_ZEBRA;
			}
			else if (i == 3)
            {
                result = EVT_THREE_ZEBRA;
			}
			else if (i == 2)
            {
                result = EVT_TWO_ZEBRA;
			}
            break;

        case LIONESS:
			if ( i == 5 )
			{
                result = EVT_FIVE_LIONESS;
			}
			else if (i == 4)
            {
                result = EVT_FOUR_LIONESS;
			}
			else if (i == 3)
            {
                result = EVT_THREE_LIONESS;
			}
			else if (i == 2)
            {
                result = EVT_TWO_LIONESS;
			}
            break;

        case LAIR:
		case BLANK:
            break;

        default:
            // Unknown icon type
            ASSERT(false);
            break;
    }


    // If we found a Lion during evaluation,
    // and we had a winning event then we had
    // a Lion substitution.  Add the note.
    if ( result > EVT_LOSER && hasLion ) 
    {
        multiplier *= MULTIPLIER_LION_SUBSTITUTE;
        spinline->addNote(NOTE_LION_SUBSTITUTION_2X);
    }
	else if ( result == EVT_LOSER && hasLion )
	{
		result = EVT_ONE_LION;
	}

    rtnresult = (uint8) result;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::classifyScatters()   
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void 
LionsLairGuts::classifyScatters(SlotSpin *spin)
{

	uint8 numLairs           	= 0;
    int32 lairScatterPositions	= 0;
	uint8 stop					= 0;
	uint8 whichItem				= BLANK;
    uint8 position				= 0;
    uint8 multiplier			= 1;
	uint8 noteid                = 0;
    
	spin->clearScatters();

	// If the current game mode is normal, then
    // check to see if the player won the progressive.
	if (getGameModeId() == MODE_NORMAL ) 
	{
		if ( getRandomProgressiveJackpotResult(spin->getSpinLineCount()) || 
			m_bForceRandomJackpot==true)
        {
	        spin->addScatter( 0, 
                              getPayout(EVT_RANDOM_PROGRESSIVE_WINNER)->payoutAmount, 
                              1,
                              true,
                              EVT_RANDOM_PROGRESSIVE_WINNER,
                              0 );
        }
    }

    // Next, check all the icons at each reel/row position.
    // We are looking for lairs.
  	for (int reel = 0; reel<5; reel++)
	{
    	for (int row = 0; row < 3; row++)
		{
            position = (row*5)+reel;

			stop = relativeWheelPosition(getGameModeId(), reel, spin->getStops()[reel], row);
			whichItem = getIcon(getGameModeId(), reel, stop)->iconid;
			
			if ( whichItem == LAIR )
			{
				if ( ( numLairs + 1 ) == reel )
				{
				   numLairs++;
				   lairScatterPositions += 1<<position;
				}
            }
		} // row
	} // reel

	if ( numLairs > 0  )
	{
		// During free spin feature there are multipliers applied.
        if ( getGameModeId() == MODE_FREE_SPIN )
        {
            noteid = NOTE_FREE_SPIN_MULTIPLIER;
            multiplier *= getFeatureGameMultiplier();
        }
	}

    // If we found any lair icons, we might
    // have a scatter winner.  If there is only
    // one lair, then this will be a scatter miss.
    // We still send a scatter record so the client
    // can highlight the position and/or play 
    // an anticipation sound on the reel.
	if (numLairs > 0 )
    {
        uint8 scatterpayouttypeid = EVT_LOSER;
		uint8 featurepayouttypeid = EVT_LOSER;
		uint8 nFreeSpins = 0;

	    switch ( numLairs )
	    {
	       case 1:
                scatterpayouttypeid = EVT_SCATTER_NEAR_MISS;
				featurepayouttypeid = EVT_FEATURE_NEAR_MISS;
                break;

           case 2: 
                scatterpayouttypeid = EVT_TWO_LAIR;
				featurepayouttypeid = EVT_FEATURE_NEAR_MISS;
                break;

	       case 3: 
                scatterpayouttypeid = EVT_THREE_LAIR;
				featurepayouttypeid = EVT_FREE_GAME_FEATURE;
				nFreeSpins = FREE_GAME_AWARD;
                break;

	       case 4: 
           case 5: 
			    // Right now there are no more than 
			    // three lairs possible.
                ASSERT(false);
                break;
	    };

        int32 nPayout = getPayout(scatterpayouttypeid)->payoutAmount * ( spin->getSpinLineCount());

        increaseTotalWin(nPayout*multiplier);

        // Let's create a scatter record for the
        // lairs.
	    SlotScatter* pScatter = spin->addScatter( lairScatterPositions, 
                                                  nPayout*getBetPerLine(),
                                                  multiplier,
                                                  false,
                                                  scatterpayouttypeid,
                                                  0 );

        // If the multiplier is > 1 report the note
        // that explains why it is greater than 1.
        if ( multiplier > 1 )
        {
            pScatter->addNote(noteid);
        }

		pScatter = spin->addScatter( lairScatterPositions,
			                         0,
									 1,
									 false,
									 featurepayouttypeid, 
									 nFreeSpins );

		if ( featurepayouttypeid == EVT_FREE_GAME_FEATURE )
		{
			incrementFeatureTriggerCount();
		}
	}

}


////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 
LionsLairGuts::isValidGuts()
{
    return (getGutsId() >= 900 && getGutsId() < 999);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )   
///
/// @param	 nGameModeMultiplier    Returns the new multiplier based on the game mode.
/// @param   nNoteId                Returns the note Id associated with the game mode.
///
/// This helper method is get the game mode note and multiplier.
////////////////////////////////////////////////////////////////////////////
void 
LionsLairGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
{

	// During free spin feature there are multipliers applied.
    if ( getGameModeId() == MODE_FREE_SPIN )
    {
        nNoteId = NOTE_FREE_SPIN_MULTIPLIER;
        nGameModeMultiplier *= getFeatureGameMultiplier();
    }
    else
    {
        SlotGuts::getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );
    }

}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::setSelectFeatureGameMultiplier(int32 bSelectFeatureGameMultiplier) 
///
/// @param   bSelectFeatureGameMultiplier  The multiplier awarded for the feature.
///
/// This is a mutator to set if the game is requesting the multiplier applied to the feature game.
////////////////////////////////////////////////////////////////////////////
void 
LionsLairGuts::setSelectFeatureGameMultiplier(int32 bSelectFeatureGameMultiplier)
{
    m_subGame->update(SGDIDX_SELECT_FEATURE_GAME_MULTIPLIER,bSelectFeatureGameMultiplier);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::getSelectFeatureGameMultiplier() const 
///
/// @return  int32   The multiplier awarded for the feature.
///
/// This is an accessor mutator method to get if the game is requesting the multiplier applied to the feature game.
////////////////////////////////////////////////////////////////////////////
int32 
LionsLairGuts::getSelectFeatureGameMultiplier() const
{
    return m_subGame->getData(SGDIDX_SELECT_FEATURE_GAME_MULTIPLIER);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::setFeatureGameMultiplier(int32 nFeatureGameMultiplier) 
///
/// @param   nFeatureGameMultiplier  The multiplier awarded for the feature.
///
/// This is a mutator to set the multiplier applied to the feature game.
////////////////////////////////////////////////////////////////////////////
void 
LionsLairGuts::setFeatureGameMultiplier(int32 nFeatureGameMultiplier)
{
    m_subGame->update(SGDIDX_FEATURE_MULTIPLIER,nFeatureGameMultiplier);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::getFeatureGameMultiplier() const 
///
/// @return  int32   The multiplier awarded for the feature.
///
/// This is an accessor mutator method to get the multiplier applied to the feature game.
////////////////////////////////////////////////////////////////////////////
int32 
LionsLairGuts::getFeatureGameMultiplier() const
{
    return m_subGame->getData(SGDIDX_FEATURE_MULTIPLIER);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::setFeatureTriggerCount(int32 nFeatureTriggerCount) 
///
/// @param   nFeatureTriggerCount  The current number of active feature triggers.
///
/// This is a mutator to set the current number of active feature triggers.
////////////////////////////////////////////////////////////////////////////
void 
LionsLairGuts::setFeatureTriggerCount(int32 nFeatureTriggerCount)
{
    m_subGame->update(SGDIDX_FEATURE_TRIGGER_COUNT,nFeatureTriggerCount);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::getFeatureTriggerCount() 
///
/// @return  int32    The current number of active feature triggers.
///
/// This is an accessor to get the current number of active feature triggers.
////////////////////////////////////////////////////////////////////////////
int32 
LionsLairGuts::getFeatureTriggerCount() const
{
    return m_subGame->getData(SGDIDX_FEATURE_TRIGGER_COUNT);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::incrementFeatureTriggerCount() 
///
/// This is a mutator to increment the current number of active feature triggers.
////////////////////////////////////////////////////////////////////////////
void 
LionsLairGuts::incrementFeatureTriggerCount()
{
    setFeatureTriggerCount(getFeatureTriggerCount()+1);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::decrementFeatureTriggerCount() 
///
/// This is a mutator to decrement the current number of active feature triggers.
////////////////////////////////////////////////////////////////////////////
void 
LionsLairGuts::decrementFeatureTriggerCount()
{
    setFeatureTriggerCount(getFeatureTriggerCount()-1);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::setFeatureMultiplierChosenMask(int32 nFeatureMultiplierChosenMask) 
///
/// @param   nFeatureTriggerCount  The mask indicating which multipliers have been used.
///
/// This is a mutator to set the mask indicating which multipliers have been used.
////////////////////////////////////////////////////////////////////////////
void 
LionsLairGuts::setFeatureMultiplierChosenMask(int32 nFeatureMultiplierChosenMask)
{
    m_subGame->update(SGDIDX_FEATURE_MULTIPLIER_CHOSEN_MASK,nFeatureMultiplierChosenMask);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::getFeatureMultiplierChosenMask() 
///
/// @return  int32    The mask indicating which multipliers have been used.
///
/// This is an accessor to get the mask indicating which multipliers have been used.
////////////////////////////////////////////////////////////////////////////
int32 
LionsLairGuts::getFeatureMultiplierChosenMask() const
{
    return m_subGame->getData(SGDIDX_FEATURE_MULTIPLIER_CHOSEN_MASK);
}



////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::initializeFeatureGame() 
///
/// @return  void.
///
/// Initialize the feature free game. Before reward the free games it validates
/// that the max payout is not reached.
////////////////////////////////////////////////////////////////////////////
void
LionsLairGuts::initializeFeatureGame()
{
	setGameModeId(MODE_FREE_SPIN);
	setFeatureGameMultiplier(0);
	setFeatureMultiplierChosenMask(0);

	// mbolanos.    07-09-24    19689: When the max payout is reached no more free games 
    // should be awarded in order to allow the free game feature and game to complete.
    // Thus before reward the free games we must validate that the max payout is not reached. 
    if(!getMaxPayoutAwarded()) 
    { 
	    incrementFreeSpins(FREE_GAME_AWARD);
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::canProcessClientData()
///
/// 
/// We can only process client data outside a spin if we are waiting
/// for the multiplier to be chosen for the next group of free spins.
/// 
////////////////////////////////////////////////////////////////////////////
bool8
LionsLairGuts::canProcessClientData() const
{
	bool8 bNeedMultiplier = false;

	if ( getGameModeId() == MODE_FREE_SPIN )
	{
		if ( getFreeSpins() > 0 )
		{
			if ( getFreeSpins() % FREE_GAMES_IN_GROUP == 0 )
			{
				bNeedMultiplier = true;
			}
		}
	}

	return bNeedMultiplier;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::processClientData()
///
/// @param   bonusGameType      The current type of bonus game.
/// @param   bonusDataCount     The number of integers passed from the client.
/// @param   bonusData			An array of integers passed from the client.
/// 
/// This is an override of the framework method.
/// 
////////////////////////////////////////////////////////////////////////////
bool8
LionsLairGuts::processClientData(uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData)
{
	// Start by assuming that we do not need to reply to this message
	bool8 bReply = false;

	if ( canProcessClientData() )
	{
		// The state will change, so a reply is required
		bReply = true;

		int32 nFreeSpinGroup = getFreeSpins() / FREE_GAMES_IN_GROUP;
		int32 nNumMultipliersChosen = getNumMultipliersChosen();
		int32 nMaxMultipliersAvailable = FREE_GAME_AWARD / FREE_GAMES_IN_GROUP;

		if ( nMaxMultipliersAvailable - nNumMultipliersChosen == nFreeSpinGroup && nFreeSpinGroup > 0 )
		{
			int32 nMultiplierIndex = bonusData[0];

			if ( nMultiplierIndex < nMaxMultipliersAvailable && !((1<<nMultiplierIndex) & getFeatureMultiplierChosenMask()) )
			{
				setFeatureGameMultiplier(ms_multipliers[nMultiplierIndex]);
				setFeatureMultiplierChosenMask((1<<nMultiplierIndex | getFeatureMultiplierChosenMask() ));
				setSelectFeatureGameMultiplier(false);
			}
		}
	}
 	
	return bReply;
}

int32
LionsLairGuts::getNumMultipliersChosen() const
{
	int32 nNumChosen = 0;
	int32 mask = 1;
	int32 multiplierChosenMask = getFeatureMultiplierChosenMask();
	int32 nMaxMultipliersAvailable = FREE_GAME_AWARD / FREE_GAMES_IN_GROUP;

	for ( int i = 0; i < nMaxMultipliersAvailable; i++ )
	{
		if ( mask & multiplierChosenMask )
		{
			nNumChosen++;
		} 

		mask = mask<<1;
	}

	return nNumChosen;
}


////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::preSpinClassifyResults()   
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden in debug spins mode to alter the reel stops.
////////////////////////////////////////////////////////////////////////////
void 
LionsLairGuts::preSpinClassifyResults( SlotSpin* spin )
{
	SlotGuts::preSpinClassifyResults(spin);

	// Check to see if a new multiplier needs to be chosen
	if ( getFreeSpins() > 0 && getFreeSpins() % FREE_GAMES_IN_GROUP == 0 )
	{
		int32 nFreeSpinGroup = getFreeSpins() / FREE_GAMES_IN_GROUP;
		int32 nNumMultipliersChosen = getNumMultipliersChosen();
		int32 nMaxMultipliersAvailable = FREE_GAME_AWARD / FREE_GAMES_IN_GROUP;

		if ( nMaxMultipliersAvailable - nNumMultipliersChosen == nFreeSpinGroup && nFreeSpinGroup > 0 )
		{
			int32 mask = 1;
			int32 multiplierChosenMask = getFeatureMultiplierChosenMask();

			for ( int i = 0; i < nMaxMultipliersAvailable; i++ )
			{
				if (( mask & multiplierChosenMask )==0)
				{
					setFeatureGameMultiplier(ms_multipliers[i]);
					setFeatureMultiplierChosenMask((multiplierChosenMask | mask ));
					break;
				} 

				mask = mask<<1;
			}		
		}
	}
}


////////////////////////////////////////////////////////////////////////////
/// @fn 	 LionsLairGuts::postSpinClassifyResults(SlotSpin* pSpin)   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
void
LionsLairGuts::postSpinClassifyResults(SlotSpin* pSpin)
{
	if ( getGameModeId() == MODE_NORMAL && getFeatureTriggerCount() > 0 )
	{
		initializeFeatureGame();
		decrementFeatureTriggerCount();
	}

    int nFreeSpins = getFreeSpins();

	if ( getGameModeId() == MODE_FREE_SPIN)
	{
        // kharmon.  08-23-05.  This game wasn't recovering correctly from a Max Payout
        // during free spin mode, so I reworked this logic a bit.
        if(nFreeSpins==0)
        {
            // we were in free spin mode, BUT we have no spins left...shift us back
            // to normal mode.
            setGameModeId( MODE_NORMAL);
            setSelectFeatureGameMultiplier(false);
            setFeatureGameMultiplier(1);
            setFeatureTriggerCount(0);  // kharmon.  08-30-05.  #6200.  We should only hit this after a max 
                                        // payout during free spin mode, after which we should also clear any 
                                        // queued features as well.

	    }
        else if(nFreeSpins%FREE_GAMES_IN_GROUP == 0)
        {
            // we're in free spin mode, but have finished one of the multiplier
            // groups, so tell the client to display the selection screen so that
            // the user can pick the next multiplier.
		    setSelectFeatureGameMultiplier(true);
        }
    }
}